package org.shj.thread.book1.filesize;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ConcurrentTotalFileSize {

	class SubDirsAndSize{
		public long size;
		public List<File> subDirs;
		
		public SubDirsAndSize(long totalSize, List<File> subDirs){
			size = totalSize;
			this.subDirs = Collections.unmodifiableList(subDirs);
		}
	}
	
	private SubDirsAndSize getTotalAndSubDirs(final File file){
		long total = 0;
		final List<File> subDirs = new ArrayList<>();
		if(file.isDirectory()){
			File[] children = file.listFiles();
			if(children != null){
				for(File child : children){
					if(child.isFile())
						total += child.length();
					else
						subDirs.add(child);
				}
			}
		}
		
		return new SubDirsAndSize(total, subDirs);
	}
	
	public long getTotalSize(final File file) throws Exception{
		final ExecutorService service = Executors.newFixedThreadPool(100);
		try{
			long total = 0;
			List<File> dirs = new ArrayList<>();
			dirs.add(file);
			
			while(!dirs.isEmpty()){
				List<Future<SubDirsAndSize>> partials = new ArrayList<>();
				
				for(final File dir : dirs){
					partials.add(service.submit(new Callable<SubDirsAndSize>(){
						public SubDirsAndSize call() {
							return getTotalAndSubDirs(dir);
						}
					}));
				}
				
				dirs.clear();
				for(Future<SubDirsAndSize> partial : partials){
					SubDirsAndSize subDirAndSize = partial.get(60, TimeUnit.SECONDS);
					dirs.addAll(subDirAndSize.subDirs);
					total += subDirAndSize.size;
				}
			}
			
			return total;
		}finally{
			service.shutdown();
		}
	}
	
	public static void main(String[] args)throws Exception{
		long start = System.nanoTime();
		String fileName = "D:\\mavenrepos\\repository\\org";
		long total = new ConcurrentTotalFileSize().getTotalSize(new File(fileName));
		long end = System.nanoTime();
		
		System.out.println("Total size: " + total);
		System.out.println("Time taken: " + (end - start)/1.0e9);
	}
}

